//
// Created by 张飞 on 2024/1/23.
//

#include <map>
#include <iostream>
#include "hv/WebSocketServer.h"
#include "hv/Event.h"
#include "hv/EventLoop.h"
#include "../sql/DDRedis.h"
#include "../sql/SQLHelper.h"
#include "hv/htime.h"
#include "../utils/DDJwtToken.h"
#include "../utils/dd_encryption.h"

using namespace hv;
using namespace std;
using namespace ddoffice;

class MyContext {
public:
    const string token;
    const string uid;
    TimerID timerID;

    MyContext() {
        printf("MyContext::MyContext()\n");
        timerID = INVALID_TIMER_ID;
    }

    MyContext(string t_token, string t_uid) : token(t_token), uid(t_uid) {
        cout << "MyContext初始化 t_token=" << t_token << " t_uid=" << t_uid << endl;
        timerID = INVALID_TIMER_ID;
    }

    ~MyContext() {
        printf("MyContext::~MyContext()\n");
    }

    int handleMessage(const std::string &msg, enum ws_opcode opcode) {

        printf("onmessage(type=%s len=%d): %.*s\n", opcode == WS_OPCODE_TEXT ? "text" : "binary",
               (int) msg.size(), (int) msg.size(), msg.data());
        return msg.size();
    }

};

void initStore() {
    SQLHelper::getInstance();
    cout << "init mysql success" << endl;
    // Connect to Redis server
    if (!DDRedis::getInstance()->connect()) {
        cout << "init redis failed" << endl;
    }
}

bool authToken(const string &token, const string &uid, string &msg){
    cout << " authToken" <<"  "<< token << "  " << uid << "  " <<  msg  << endl;
    if (token.empty() || uid.empty()) {
        msg = "token and account is required";
        return false;
    }
    cout << "  ddredis get " << endl;
    string l_token = DDRedis::getInstance()->get(dd_encryption::calculateMD5(uid));
    if (l_token.empty())
    {
        msg = "token 无效";
        return false;
    }
    
    cout << "local token="<< l_token << endl;
    return DDJwtToken().verifyTokenWithSignature(l_token, uid, token, msg);
}

int main() {
    initStore();
    cout << "im start success" << endl;
    HttpService httpService;
    httpService.GET("/ping", [](const HttpContextPtr &ctx) {
        cout << "request ping response pong" << endl;
        return ctx->send("pong");
    });
    WebSocketService ws;
    
    ws.onopen = [](const WebSocketChannelPtr &channel, const HttpRequestPtr &req) {
        http_headers head = req->headers;
        string tok = head["token"];
        string uid = head["account"];
        cout << "onopen token=" << tok << "  account=" << uid << "  ------->>> url=" << req->Url() << endl;
        auto ctx = make_shared<MyContext>(tok, uid);
        channel->setContextPtr(ctx);
        string msg = "";
        if (!authToken(tok, uid, msg))
        {
            channel->send(msg);
            channel->close();
            return;
        }
        // send(time) every 1s
        ctx->timerID = setInterval(1000, [channel](TimerID id) {
            if (channel->isConnected() && channel->isWriteComplete()) {
                char str[DATETIME_FMT_BUFLEN] = {0};
                datetime_t dt = datetime_now();
                datetime_fmt(&dt, str);
                channel->send(str);
            }
        });
    };

    ws.onmessage = [](const WebSocketChannelPtr &channel, const std::string &msg) {
        // cout << "onmessage size=" << msg.size() << "------->>> " << msg << endl;
        if (msg == "close") {
            channel->close();
            cout << "im closed" << endl;
            return;
        }

        auto ctx = channel->getContextPtr<MyContext>();
        ctx->handleMessage(msg, channel->opcode);
    };
    ws.onclose = [](const WebSocketChannelPtr &channel) {
        cout << "onclose" << endl;
        auto ctx = channel->getContextPtr<MyContext>();
        if (ctx->timerID != INVALID_TIMER_ID) {
            killTimer(ctx->timerID);
            ctx->timerID = INVALID_TIMER_ID;
        }
        // channel->deleteContextPtr();
    };

    WebSocketServer server;
    server.port = 9999;

    server.registerHttpService(&httpService);
    server.registerWebSocketService(&ws);
    server.run();

    return 0;
}
